#include "GenomeInBits.h"

CGenomeInBits::CGenomeInBits(unsigned int uiGenomeSize) : CSeqInBits(uiGenomeSize)
{
    this->pGenome = NULL;
    this->uiSeqLengthInWordSize = lengthInWordSize(this->uiSeqLength); // add one more
    this->caSubstring[0] = '\0';
}

CGenomeInBits::~CGenomeInBits(void)
{
}

CGenomeInBits::CGenomeInBits(CGenomeNTdata* pGenome) : CSeqInBits((int)pGenome->iGenomeSize)
{
    this->pGenome = pGenome;
    this->uiSeqLengthInWordSize = lengthInWordSize(this->uiSeqLength); // add one more
    this->caSubstring[0] = '\0';
    // If there are non-ACGT characters in a chromosome, the corresponding bits in memory will be set to A (00).

    for (unsigned int chrId = 0; chrId < this->pGenome->iNo_of_chromosome; chrId++) {
        CchromosomeNTdata* chr = this->pGenome->paChromosomes[chrId];
        // (1) Encode the junction of each chromosome
        encodeJunction(chrId);

        // (2) Encode the middle part of the chromosome
        unsigned int uiGenomeLoucs = this->pGenome->chrIndex2genomelocusID(chrId, 0);
        unsigned int blockId = (uiGenomeLoucs / wordSize) + 1; // start at the second encoding block for the chromosome
        uiGenomeLoucs = blockId * wordSize ; // The start genome Locus Id of the block
        unsigned int chrLocusId = this->pGenome->genomeLocusID2chrIndex(uiGenomeLoucs);
        unsigned int i;
        // i is the block local Id in the chromosome. blockId is the global bloack Id
        for (i = 1; chrLocusId + i * wordSize < chr->iChromosome_size; blockId++, i++) {
            encodeReadNasA(&(chr->caChromosome[chrLocusId + (i - 1) * wordSize]), wordSize,
                           &this->pUpperBits[blockId], &this->pLowerBits[blockId]);
        }
        // (3) Encode the tail of the chromosome (won't be empty)
        encodeReadNasA(&(chr->caChromosome[chrLocusId + (i - 1) * wordSize]), chr->iChromosome_size - (chrLocusId + (i - 1) * wordSize),
                       &this->pUpperBits[blockId], &this->pLowerBits[blockId]);
    }
    // (4) Encode bits N in a boolFlagArray
    unsigned int noOfNInGenome = 0;
    for (unsigned int chrId = 0; chrId < this->pGenome->iNo_of_chromosome; chrId++) {
        CchromosomeNTdata* chr = this->pGenome->paChromosomes[chrId];
        unsigned int uiGenomeLoucs = this->pGenome->chrIndex2genomelocusID(chrId, 0);
        for (unsigned int i = 0; i < chr->iChromosome_size; i++, uiGenomeLoucs++) {
            if (!isACGT(chr->caChromosome[i])) {
                this->pNBits->setflag(uiGenomeLoucs, true);
                noOfNInGenome ++;
            }
        }
    }
    if ( noOfNInGenome > 0) {
        LOG_INFO("Info %d: There are %u N in the genome.\r", INFO_LOG, noOfNInGenome);
    }
}

int CGenomeInBits::encodeJunction(unsigned int chrId)
{
    char region[wordSize + 1];
    unsigned int uiGenomeLoucs = this->pGenome->chrIndex2genomelocusID(chrId, 0);
    unsigned int blockId = uiGenomeLoucs / wordSize;

    if (chrId == 0) {
        strncpy(region, this->pGenome->paChromosomes[chrId]->caChromosome, wordSize);
    } else {
        unsigned int tailLength = uiGenomeLoucs % wordSize; // tail of the previous chromosome in the junction block
        CchromosomeNTdata* pChr;
        pChr = this->pGenome->paChromosomes[chrId - 1];
        strncpy(region, &(pChr->caChromosome[pChr->iChromosome_size - tailLength]), tailLength);
        pChr = this->pGenome->paChromosomes[chrId];
        strncpy(&region[tailLength], pChr->caChromosome, wordSize - tailLength);
    }
    region[wordSize] = '\0';
    encodeReadNasA(region, wordSize, &this->pUpperBits[blockId], &this->pLowerBits[blockId]);
    return(0);
}

char* CGenomeInBits::getSubstring(unsigned int uiGenomeIndex)
{
    CReadInBits r = getSubstringInBits(uiGenomeIndex);
    decodeRead(caSubstring, wordSize, r.UpperBits, r.LowerBits);
    return (caSubstring);
}

char* CGenomeInBits::getSubstring(unsigned int uiGenomeIndex, unsigned int uiSubstringLength)
{
    CReadInBits r = getSubstringInBits(uiGenomeIndex);
    decodeRead(caSubstring, wordSize, r.UpperBits, r.LowerBits);
    if (uiSubstringLength <= wordSize) {
        caSubstring[uiSubstringLength] = '\0';
    }

    if (uiGenomeIndex +  uiSubstringLength > uiSeqLength) {
        if (uiSeqLength > uiGenomeIndex)
            caSubstring[uiSeqLength - uiGenomeIndex] = '\0';
        else {
            cout << "Warning, Wrong genome index " << endl;
            caSubstring[0] = '\0';
        }
    }
    return (caSubstring);
}

bool CGenomeInBits::fragACGTKmerInBits(CReadInBits& kmerInBits, int startIndex, int kmerLength)
{
    for (int i = 0; i < kmerLength; i++) {
        if (this->pNBits->b(i + startIndex)) { // Meet N
            return(false);
        }
    }
    kmerInBits = getSubstringInBits(startIndex, kmerLength);
    return(true);
}
